package com.nowcoder.community.config;

import com.nowcoder.community.util.CommunityConstant;
import com.nowcoder.community.util.GenerateUtil;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * @BelongsProject: community
 * @BelongsPackage: com.nowcoder.community.config
 * @Author: Master_XXJS
 * @CreateTime: 2023-01-09  19:07
 * @Description: TODO
 * @Version: 1.0
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter implements CommunityConstant {

    @Override
    public void configure(WebSecurity web) throws Exception {
        //忽略静态资源
        web.ignoring().antMatchers("/resources/**");
    }

    //下面我们没有开启表单认证，也就是我们登录还是我们自己写的代码逻辑
    // 只不过判断 是否登录 和 有无权限访问 换成了 security 管理
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                //下面的路径，登陆之后必须有下面的任意的权限才能进行访问
                .antMatchers(
                    "/user/setting",
                        "/user/upload",
                        "/discuss/add",
                        "comment/add",
                        "/letter/**",
                        "/notice/**",
                        "/like",
                        "follow",
                        "unfollow"
                )
                .hasAnyAuthority(
                        AUTHORITY_ADMIN,
                        AUTHORITY_USER,
                        AUTHORITY_MODERATOR
                )
                .antMatchers(
                        "/discuss/top",
                        "/discuss/wonderful"
                )
                .hasAnyAuthority(
                        //只有版主才能访问上面的两路径，置顶，加精
                        AUTHORITY_MODERATOR
                )
                .antMatchers(
                        "/discuss/delete",
                        "/data/**",
                        "/actuator/**"
                )
                .hasAnyAuthority(
                        //只有管理员才能访问上面的路径，删除
                        AUTHORITY_ADMIN
                )
                .anyRequest().permitAll()
                .and().csrf().disable(); //其他路径没有权限也可以进行访问

        //权限不够的时候
        http.exceptionHandling()
                .authenticationEntryPoint(new AuthenticationEntryPoint() {
                    //没有登录的时候的处理
                    @Override
                    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
                        //通过 request 获取 请求的 请求方式的 header （类型）
                        String XRequestedWith = httpServletRequest.getHeader("x-requested-with");
                        //如果是和 XML 一样的话，就是异步请求（返回JSON），不然是同步请求（返回Html）
                        if ("XMLHttpRequest".equals(XRequestedWith)){
                            //异步请求（返回jSON）
                            // plain 表示返回的是 普通字符串
                            httpServletResponse.setContentType("application/plain;charset=utf-8");
                            PrintWriter writer = httpServletResponse.getWriter();
                            writer.write(GenerateUtil.getJsonString(403,"你没有登录哦！"));
                        }else {
                            //同步请求（返回Html）
                            //request 获取项目的 路径
                            httpServletResponse.sendRedirect(httpServletRequest.getContextPath()+"/login");
                        }
                    }
                })
                .accessDeniedHandler(new AccessDeniedHandler() {
                    //权限不够的时候
                    @Override
                    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
                        //获取 请求的 请求方式的 header （类型）
                        String XRequestedWith = httpServletRequest.getHeader("x-requested-with");
                        //如果是和 XML 一样的话，就是异步请求（返回JSON），不然是同步请求（返回Html）
                        if ("XMLHttpRequest".equals(XRequestedWith)){
                            //异步请求（返回jSON）
                            // plain 表示返回的是 普通字符串
                            httpServletResponse.setContentType("application/plain;charset=utf-8");
                            PrintWriter writer = httpServletResponse.getWriter();
                            writer.write(GenerateUtil.getJsonString(403,"你没有访问该功能的权限！"));
                        }else {
                            //同步请求（返回Html）
                            httpServletResponse.sendRedirect(httpServletRequest.getContextPath()+"/denied");
                        }
                    }
                });

        //security 底层默认的是会拦截 /logout 请求，进行退出处理
        //覆盖他的默认退出逻辑，才能执行我们的退出的代码
        //因为我们的登出也是 /logout 地址
        //要不就是我们修改登出的地址，要不就是将 security 拦截的登出地址改了，只要我们地址不一样就行
        http.logout().logoutUrl("/securitylogout"); //将 security 登出 的默认地址改了
    }
}

